/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.qiujuer.genius.ui.widget;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.render.Paint;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.app.Environment;
import ohos.global.resource.RawFileEntry;
import ohos.global.resource.Resource;
import ohos.global.resource.ResourceManager;

import net.qiujuer.genius.ui.drawable.shape.BorderShape;
import net.qiujuer.genius.ui.element.ShapeElement;
import net.qiujuer.genius.ui.util.AttrValue;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Text
 *
 * @since 2021-05-10
 */
@SuppressWarnings("WeakerAccess")
public class Text extends ohos.agp.components.Text {
    /**
     * border left flag
     */
    public static final int BORDER_LEFT = 0x0001;
    /**
     * border right flag
     */
    public static final int BORDER_RIGHT = 0x0010;
    /**
     * border top flag
     */
    public static final int BORDER_TOP = 0x0100;
    /**
     * border bottom flag
     */
    public static final int BORDER_BOTTOM = 0x1000;
    /**
     * border all flag
     */
    public static final int BORDER_ALL = BORDER_LEFT | BORDER_RIGHT | BORDER_TOP | BORDER_BOTTOM;
    private int mBorder;
    private int mBorderColor;
    private int mBorderSize;
    private ShapeElement mBorderDrawable;
    private DrawTask mDrawTask = (component, canvas) -> {
        ShapeElement drawable = mBorderDrawable;
        if (drawable != null && mBorderSize > 0 && mBorderColor != 0) {
            int width = getEstimatedWidth();
            int height = getEstimatedHeight();
            drawable.getBorderShape().resize(width, height);
            drawable.draw(canvas);
        }
    };

    /**
     * constructor
     *
     * @param context
     */
    public Text(Context context) {
        super(context);
        addDrawTask(mDrawTask, DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
    }

    /**
     * constructor
     *
     * @param context
     * @param attrSet
     */
    public Text(Context context, AttrSet attrSet) {
        super(context, attrSet);
        addDrawTask(mDrawTask, DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
        init(attrSet);
    }

    /**
     * constructor
     *
     * @param context
     * @param attrSet
     * @param defStyleAttr
     */
    public Text(Context context, AttrSet attrSet, int defStyleAttr) {
        super(context, attrSet, "defStyleAttr");
        addDrawTask(mDrawTask, DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
        init(attrSet);
    }

    private void init(AttrSet attrSet) {
        if (attrSet == null) {
            return;
        }
        final Context context = getContext();
        final float density = DisplayManager.getInstance().getDefaultDisplay(context)
            .get().getAttributes().densityPixels;
        int border = 0;
        int borderSize = (int) density;
        int borderColor = Color.getIntColor("#9e9e9e");
        if (attrSet.getAttr("gBorder").isPresent()) {
            String borderValue = AttrValue.get(attrSet, "gBorder", "none");
            border = getBorder(borderValue);
        }
        if (attrSet.getAttr("gBorderSize").isPresent()) {
            borderSize = AttrHelper.fp2px(AttrValue.get(attrSet, "gBorderSize", (int) density), context);
        }
        if (attrSet.getAttr("gBorderColor").isPresent()) {
            Color borderColorValue = AttrValue.get(attrSet, "gBorderColor", new Color(borderColor));
            borderColor = borderColorValue.getValue();
        }
        setBorder(border, borderSize, borderColor);

        if (attrSet.getAttr("gFont").isPresent()) {
            String fontFile = AttrValue.get(attrSet, "gFont", "");
            if (fontFile != null && fontFile.length() > 0) {
                Font font = createFontBuild(context, fontFile);
                setFont(font);
            }
        }
    }

    private int getBorder(String borderValue) {
        int border = 0;
        if (borderValue.indexOf("|") != -1) {
            String[] borderValues = borderValue.split("\\|");
            int[] values = new int[borderValues.length];
            for (int i = 0; i < borderValues.length; i++) {
                values[i] = getBorder(borderValues[i]);
            }
            switch (values.length) {
                case 2:
                    border = values[0] | values[1];
                    break;
                case 3:
                    border = values[0] | values[1] | values[2];
                    break;
                case 4:
                    border = values[0] | values[1] | values[2] | values[3];
                    break;
                default:
            }
            return border;
        } else {
            switch (borderValue) {
                case "none":
                    border = 0;
                    break;
                case "all":
                    border = 0x1111;
                    break;
                case "left":
                    border = 0x0001;
                    break;
                case "top":
                    border = 0x0100;
                    break;
                case "right":
                    border = 0x0010;
                    break;
                case "bottom":
                    border = 0x1000;
                    break;
                default:
            }
        }
        return border;
    }

    /**
     * Set the border, You can init border size, color and decision that direction contains border
     *
     * @param flag use {@link #BORDER_ALL } if you need All border,
     * or {@link #BORDER_LEFT} the view will have Left border
     * of case you can use eg: {@link #BORDER_LEFT}|{@link #BORDER_BOTTOM}
     * the view will left and bottom border
     * <p>
     * if you not use border you need set the flag to 0
     * @param size set all border size,unit is px
     * @param color set all border color
     */
    public void setBorder(int flag, int size, int color) {
        if (mBorder != flag || mBorderSize != size || mBorderColor != color) {
            mBorder = flag;
            mBorderSize = size;
            mBorderColor = color;

            if (flag <= 0) {
                mBorderDrawable = null;
            } else {
                Rect borderRect;
                if ((flag & BORDER_ALL) == BORDER_ALL) {
                    borderRect = new Rect(size, size, size, size);
                } else {
                    int l = 0, t = 0, r = 0, b = 0;
                    if ((flag & BORDER_LEFT) == BORDER_LEFT) {
                        l = size;
                    }
                    if ((flag & BORDER_RIGHT) == BORDER_RIGHT) {
                        r = size;
                    }
                    if ((flag & BORDER_TOP) == BORDER_TOP) {
                        t = size;
                    }
                    if ((flag & BORDER_BOTTOM) == BORDER_BOTTOM) {
                        b = size;
                    }
                    borderRect = new Rect(l, t, r, b);
                }
                if (mBorderDrawable == null) {
                    BorderShape shape = new BorderShape(borderRect);
                    ShapeElement drawable = new ShapeElement(shape);
                    Paint paint = drawable.getPaint();
                    paint.setColor(new Color(color));
                    mBorderDrawable = drawable;
                } else {
                    ShapeElement drawable = mBorderDrawable;
                    Paint paint = drawable.getPaint();
                    paint.setColor(new Color(color));
                    BorderShape shape = drawable.getBorderShape();
                    shape.setBorder(borderRect);
                }
            }
        }
    }

    /**
     * Get all border color
     *
     * @return color value
     */
    public int getBorderColor() {
        return mBorderColor;
    }

    /**
     * Get all border size
     *
     * @return px value
     */
    public int getBorderSize() {
        return mBorderSize;
    }

    /**
     * 使用流解析解析ttf文件
     *
     * @param context 上下文
     * @param name ttf文件名字
     * @return 解析后的Font
     */
    public static Font createFontBuild(Context context, String name) {
        ResourceManager resManager = context.getResourceManager();
        RawFileEntry rawFileEntry = resManager.getRawFileEntry("resources/rawfile/" + name);
        Resource resource = null;
        try {
            resource = rawFileEntry.openRawFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        StringBuffer fileName = new StringBuffer(name);
        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName.toString());
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(file);
            int index;
            byte[] bytes = new byte[1024];
            if(resource == null){
                resource = rawFileEntry.openRawFile();
            }
            while ((index = resource.read(bytes)) != -1) {
                outputStream.write(bytes, 0, index);
                outputStream.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(resource != null){
                    resource.close();
                }
                if(outputStream != null){
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        Font.Builder builder = new Font.Builder(file);
        return builder.build();
    }
}
